<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * This file contains the PDODB_Exception class and its subclasses.
 *
 * This file contains the following classes:
 *
 * <ul>
 * <li>PDODB_Exception</li>
 * <li>PDODB_ConnectionException</li>
 * <li>PDODB_FileReadException</li>
 * <li>PDODB_FileNotFoundException<l/i>
 * <li>PDODB_ClassNotFoundException</li>
 * <li>PDODB_InstanceNotFoundException</li>
 * <li>PDODB_UnsupportedDriverException</li>
 * <li>PDODB_ErrorModeException</li>
 * <li>PDODB_UnsupportedFeatureException</li>
 * <li>PDODB_LoadModuleException</li>
 * <li>PDODB_InvalidRangeException</li>
 * <li>PDODB_UnknownDeclarationTypeException</li>
 * <li>PDODB_InsufficientDataException</li>
 * </ul>
 *
 * PHP version 5
 *
 * This file is part of PEAR::PDODB - A database abstraction layer for PHP5 built
 * using PDO. PHP version 5.1+ is recommended.
 *
 * PEAR::PDODB is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * PEAR::PDODB is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with PEAR::PDODB. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package     PDODB
 * @category    Database
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @copyright   2007 Michael J. I. Jackson
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
 * @version     SVN: $Id: Exception.php 2 2007-11-27 17:20:29Z mjijackson $
 * @link        http://pear.php.net/packages/PDODB
 */

/**
 * Require PEAR_Exception
 */
require_once 'PEAR' . DIRECTORY_SEPARATOR . 'Exception.php';

/**
 * The base exception class used by PDODB. Should be extended by all other PDODB
 * exception classes.
 *
 * @package     PDODB
 * @category    Database
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @see         PEAR_Exception
 */
class PDODB_Exception extends PEAR_Exception
{

    /**
     * Gets an array of all causes that triggered this exception. This function
     * must be public on all child classes.
     *
     * @param   array   $causes     An array to populate with exception causes
     * @return  void
     * @access  public
     */
    public function getCauseMessage(&$causes)
    {
        $causes[] = array(
            'class'     => get_class($this),
            'message'   => $this->getMessage(),
            'file'      => $this->getFile(),
            'line'      => $this->getLine()
        );

        if ($this->cause instanceof PEAR_Exception) {
            $this->cause->getCauseMessage($causes);
        } elseif ($this->cause instanceof Exception) {
            $causes[] = array(
                'class'     => get_class($this->cause),
                'message'   => $this->cause->getMessage(),
                'file'      => $this->cause->getFile(),
                'line'      => $this->cause->getLine()
            );
        } elseif (class_exists('PEAR_Error') && $this->cause instanceof PEAR_Error) {
            $causes[] = array(
                'class'     => get_class($this->cause),
                'message'   => $this->cause->getMessage(),
                'file'      => 'unknown',
                'line'      => 'unknown'
            );
        } elseif (is_array($this->cause)) {
            foreach ($this->cause as $cause) {
                if ($cause instanceof PEAR_Exception) {
                    $cause->getCauseMessage($causes);
                } elseif ($cause instanceof Exception) {
                    $causes[] = array(
                        'class'     => get_class($cause),
                        'message'   => $cause->getMessage(),
                        'file'      => $cause->getFile(),
                        'line'      => $cause->getLine()
                    );
                } elseif (class_exists('PEAR_Error') && $cause instanceof PEAR_Error) {
                    $causes[] = array(
                        'class'     => get_class($cause),
                        'message'   => $cause->getMessage(),
                        'file'      => 'unknown',
                        'line'      => 'unknown'
                    );
                } elseif (is_array($cause) && isset($cause['message'])) {
                    // PEAR_ErrorStack warning
                    $causes[] = array(
                        'class'     => $cause['package'],
                        'message'   => $cause['message'],
                        'file'      => isset($cause['context']['file'])
                            ? $cause['context']['file']
                            : 'unknown',
                        'line'      => isset($cause['context']['line'])
                            ? $cause['context']['line']
                            : 'unknown',
                    );
                }
            }
        }
    }

    /**
     * Gets a HTML string representation of this exception.
     *
     * @return  string              An HTML string representation of this
     *                              exception
     * @access  public
     */
    public function toHTML()
    {
        $causes = array();
        $this->getCauseMessage($causes);
        $html = '<div style="font-family: \'Lucida Grande\', Verdana, sans-serif; font-size: 12px; margin: 40px; padding: 12px 20px; color: #333; border: 1px solid #999;">' . "\n"
            . '<h1 style="font-size: 14px; color: #a00;">Uncaught Exception</h1>' . "\n";
        foreach ($causes as $i => $cause) {
            if ($i != 0) {
                $html .= "^<br />\n";
            }
            $html .= "<b>" . $cause['class'] . '</b>: ' . htmlspecialchars($cause['message']) . "<br />\n"
                . "file: " . $cause['file'] . "<br />\n"
                . "line: " . $cause['line'] . "<br />\n";
        }

        $trace = $this->getTraceSafe();
        $html .= '<h1 style="font-size: 14px; color: #a00;">Exception Trace</h1>' . "\n";
        foreach ($trace as $i => $t) {
            if ($i != 0) {
                $html .= "^<br />\n";
            }
            $args = array();
            if (!empty($t['args'])) {
                foreach ($t['args'] as $arg) {
                    if (is_null($arg)) $args[] = 'null';
                    elseif (is_array($arg)) $args[] = 'Array';
                    elseif (is_object($arg)) $args[] = 'Object(' . get_class($arg) . ')';
                    elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false';
                    elseif (is_int($arg) || is_double($arg)) $args[] = $arg;
                    else {
                        $arg = (string)$arg;
                        $str = htmlspecialchars(substr($arg, 0, 16));
                        if (strlen($arg) > 16) $str .= '&hellip;';
                        $args[] = "'" . $str . "'";
                    }
                }
            }

            $signature = empty($t['class']) ? '' : $t['class'] . $t['type'];
            $html .= '<b>' . $signature . $t['function'] . '</b>(' . implode(', ',$args) . ")<br />\n"
                . "file: " . (empty($t['file']) ? 'unknown' : $t['file']) . "<br />\n"
                . "line: " . (empty($t['line']) ? 'unknown' : $t['line']) . "<br />\n";
        }
        $html .= "</div>\n";

        return $html;
    }

}

/**
 * Connect exception. Thrown in PDODB_Connection::connect() when PDODB is unable
 * to establish a connection to the database.
 *
 * @package     PDODB
 * @see         PDODB_Connection_Exception
 */
class PDODB_ConnectException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   PDODB_Connection_Exception      $e      The cause of the error
     * @access  public
     */
    public function __construct(Exception $e)
    {
        parent::__construct('Connection error', $e);
    }

}

/**
 * File read exception. Thrown by PDODB::resolveDSN() when the DSN specifies a
 * file that cannot be read.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_FileReadException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string  $file       The file name
     * @access  public
     */
    public function __construct($file)
    {
        parent::__construct("File '$file' could not be read");
    }

}

/**
 * File not found exception. Thrown by PDODB::loadClass() when the file
 * corresponding to the requested class name cannot be found or by
 * PDODB::resolveDSN() when the DSN specifies a file that cannot be found.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_FileNotFoundException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string  $file       The file name
     * @access  public
     */
    public function __construct($file)
    {
        parent::__construct("File '$file' was not found");
    }

}

/**
 * Class not found exception. Thrown by PDODB::loadClass() when the requested class
 * was not found.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_ClassNotFoundException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string  $class      The class name
     * @param   string  $file       The file name
     * @access  public
     */
    public function __construct($class, $file = null)
    {
        $message = "Class '$class' was not found";
        if ($file) {
            $message .= " in file '$file'";
        }

        parent::__construct($message);
    }

}

/**
 * Instance not found exception. Thrown by PDODB::getConnection() and
 * PDODB_Domain::getConnection() when the desired database connection instance
 * can't be found.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_InstanceNotFoundException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   mixed       $conn_index       The index that was searched for
     * @access  public
     */
    public function __construct($conn_index)
    {
        parent::__construct("Error retrieving connection instance ($conn_index)");
    }

}

/**
 * Unsupported driver exception. Thrown when the requested driver is not yet
 * supported by PDODB.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_UnsupportedDriverException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string              $driver     The driver name
     * @param   PDODB_Exception     $e          The original exception
     * @access  public
     */
    public function __construct($driver, PDODB_Exception $e = null)
    {
        $msg = "Unsupported database driver '$driver'";
        if ($e) {
            parent::__construct($msg, $e);
        } else {
            parent::__construct($msg);
        }
    }

}

/**
 * Unsupported feature exception. Thrown when the user requests a feature that
 * is not supported on the driver being used.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_UnsupportedFeatureException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string      $feature        The requested feature
     * @param   string      $driver         The driver name
     * @access  public
     */
    public function __construct($feature, $driver)
    {
        parent::__construct("Driver '$driver' does not support feature '$feature'");
    }

}

/**
 * Load module exception. Thrown by PDODB_Connection::loadModule() when the requested
 * module fails to load.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_LoadModuleException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string          $module     The requested module
     * @param   PDODB_Exception $e          The original exception
     * @access  public
     */
    public function __construct($module, $e)
    {
        parent::__construct("Error loading '$module' module", $e);
    }

}

/**
 * Invalid range exception. Thrown when invalid range parameters are passed to
 * PDODB_Connection::setLimit().
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_InvalidRangeException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string      $msg        The error message
     * @access  public
     */
    public function __construct($msg)
    {
        parent::__construct("Invalid range: $msg");
    }

}

/**
 * Unknown declaration type exception. Thrown when the field type passed to
 * PDODB_Datatype::getDeclaration() is unknown.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_UnknownDeclarationTypeException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string  $type       The type of field that was declared
     * @access  public
     */
    public function __construct($type)
    {
        parent::__construct("Unknown field type '$type'");
    }

}

/**
 * Insufficient data exception. Thrown when the user does not supply sufficient
 * data to complete the desired operation.
 *
 * @package     PDODB
 * @see         PDODB_Exception
 */
class PDODB_InsufficientDataException extends PDODB_Exception
{

    /**
     * Constructor.
     *
     * @param   string  $msg            The error message
     * @access  public
     */
    public function __construct($msg)
    {
        parent::__construct("Insufficient data: $msg");
    }

}

?>
